package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tbit.common.constant.CommonConstant;
import com.tbit.common.entity.UserOpLog;
import com.tbit.common.entity.view.PageResult;
import com.tbit.common.entity.view.Result;
import com.tbit.common.utils.Utf8Mb4Checker;
import com.tbit.uqbike.constant.*;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.pojo.dto.EditOrderDTO;
import com.tbit.uqbike.object.pojo.dto.FinishOrderDTO;
import com.tbit.uqbike.object.pojo.dto.RideLogDetailsDownloadDTO;
import com.tbit.uqbike.object.pojo.dto.RideLogDetailsQueryDTO;
import com.tbit.uqbike.object.pojo.vo.*;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.base.RedisService;
import com.tbit.uqbike.service.business.OrderFeeAppealService;
import com.tbit.uqbike.service.business.RefundOrderService;
import com.tbit.uqbike.service.business.RideLogDetailsService;
import com.tbit.uqbike.webmanager.business.machine_op_log.service.MachineOpLogService;
import com.tbit.uqbike.webmanager.business.user_op_log.service.UserOpLogService;
import com.tbit.uqbike.webmanager.dao.capital.*;
import com.tbit.uqbike.webmanager.dao.core.*;
import com.tbit.uqbike.webmanager.dao.log.RefundOpeLogDao;
import com.tbit.uqbike.webmanager.dao.log.RideOrderMoneyOpeLogDao;
import com.tbit.uqbike.webmanager.util.BeanUtils;
import com.tbit.uqbike.webmanager.util.MD5PasswordUtil;
import com.tbit.uqbike.webmanager.util.csv.CsvExportUtils;
import com.tbit.uqbike.webmanager.util.csv.entity.ExportHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.IntSupplier;
import java.util.stream.Collectors;

/**
 * @Author:BUGTian
 * @DATE: 2022/11/12
 */
@Service
@Slf4j
public class RideLogDetailsServiceImpl implements RideLogDetailsService {

    @Autowired
    CsvExportUtils csvExportUtils;
    @Autowired
    private RideLogDetailsDao rideLogDetailsDao;
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private AccountUserDao accountUserDao;
    @Autowired
    private HolderAccountBoundDao holderAccountBoundDao;
    @Autowired
    private RideLogDao rideLogDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private UserAccountLogDao userAccountLogDao;
    @Autowired
    private UserCapitalChangeDao userCapitalChangeDao;
    @Autowired
    private UserAccountMoneyDao userAccountMoneyDao;
    @Autowired
    private BalanceChangeDao balanceChangeDao;
    @Autowired
    private ArrearsLogDao arrearsLogDao;
    @Autowired
    private MoneyOpeLogDao moneyOpeLogDao;
    @Autowired
    private RideOrderMoneyOpeLogDao rideOrderMoneyOpeLogDao;
    @Autowired
    private RefundLogDao refundLogDao;
    @Autowired
    private UserToAgentDao userToAgentDao;
    @Autowired
    private UserDelayPayDao userDelayPayDao;
    @Autowired
    private RideUseLogDao rideUseLogDao;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private MachineOpLogService machineOpLogService;
    @Autowired
    private MachineDao machineDao;
    @Resource
    private MachineBorrowDao machineBorrowDao;
    @Autowired
    private UserOpLogService userOpLogService;
    @Resource
    private RideCardDao rideCardDao;
    @Resource
    private VipCardDao vipCardDao;
    @Resource
    private CouponDao couponDao;
    @Resource
    private OrderFeeAppealDao orderFeeAppealDao;
    @Resource
    private OrderFeeAppealService orderFeeAppealService;


    @Override
    public PageResult<RideLogDetails> queryPage(RideLogDetailsQueryDTO queryDTO) {
        Map<String, String> map = new HashMap<>();
        map.put("1", "站外停车");
        map.put("2", "运营区域外停车");
        map.put("3", "头盔锁未归还");
        map.put("5", "禁停区罚款");
        PageInfo<RideLogDetails> pageInfo = PageHelper.startPage(queryDTO.getPage(), queryDTO.getSize()).doSelectPageInfo(() -> rideLogDetailsDao.selectList(queryDTO));
        for (RideLogDetails rideLogDetails : pageInfo.getList()) {
            if (rideLogDetails.getFineTypeName() != null && !"".equals(rideLogDetails.getFineTypeName())) {
                String fineTypeName = Arrays.stream(rideLogDetails.getFineTypeName().split(",")).map(map::get).collect(Collectors.joining(","));
                rideLogDetails.setFineTypeName(fineTypeName);
            }
        }
        return new PageResult<>(queryDTO.getPage(), queryDTO.getSize(), pageInfo.getTotal(), pageInfo.getPages(), pageInfo.getList());
    }

    @Override
    public RideLogDetailsSumVO sum(RideLogDetailsQueryDTO queryDTO) {
        return rideLogDetailsDao.sum(queryDTO);
    }

    @Override
    public PageResult<RideLogDetailsVO> queryCyclingRecord(LoginInfo login, String phone, String userCode, Integer page, Integer size) {
        AccountAuth accountAuth = getAccountAuth(login);
        PageInfo<RideLogDetailsVO> pageInfo = PageHelper.startPage(page, size).doSelectPageInfo(() -> rideLogDetailsDao.queryCyclingRecord(accountAuth.getBrandId(), accountAuth.getAccountIdList(), phone, userCode));
        return new PageResult<>(page, size, pageInfo.getTotal(), pageInfo.getPages(), pageInfo.getList());
    }

    /**
     * 获取区域权限限定
     *
     * @param login
     * @return
     */
    @Override
    public AccountAuth getAccountAuth(LoginInfo login) {
        Integer brandId = null;
        List<Integer> accountIdList = new ArrayList<>();
        Integer accountId = login.getId();
        if (UserConstant.USER_BRAND.equals(login.getType())) {
            //品牌账号，能查询所有区域的
            brandId = accountId;
            AccountUserBound accountUserBound = accountUserDao.queryAccountUserBound(login.getAccountUserId(), brandId);
            if (accountUserBound != null) {
                if (UserConstant.USER_CUS_HOLDER.equals(accountUserBound.getSubRoleType())) {
                    //股东账号，只能查询绑定的区域
                    accountIdList = holderAccountBoundDao.selectAccountIdList(login.getAccountUserId());
                    //防止list为空，不判断，导致查看了所有区域
                    accountIdList.add(-2);
                }
            }
        } else if (UserConstant.USER_FRANCHISEE.equals(login.getType())) {
            //加盟商，只允许查询加盟商下的区域
            accountIdList = accountDao.getAccountByParentId(login.getId());
        } else if (UserConstant.USER_AGENT.equals(login.getType())) {
            //区域，只允许查看当前区域
            accountIdList.add(accountId);
        }
        return new AccountAuth(brandId, accountIdList);
    }

    @Override
    public Result finishOrder(LoginInfo login, FinishOrderDTO finishOrderDTO) {
        if (finishOrderDTO.getMoney() < 0) {
            return Result.error("订单金额有误");
        }
        //查询用户最新一条订单
        RideLog rideLog = rideLogDao.queryLatestRideLog(finishOrderDTO.getUserId());
        if (rideLog == null) {
            return Result.error("订单不存在");
        }
        if (rideLog.getEndTime() != null) {
            return Result.error("订单已结束,请勿重复结束订单");
        }
        //调用客户端接口进行结束订单前判断是否包含特殊字符
        if (Utf8Mb4Checker.containsUtf8mb4Chars(finishOrderDTO.getRemark())) {
            throw new BaseException("备注含非法字符，请调整后再次发起");
        }

        /*使用卡券抵扣*/
        if (Objects.nonNull(finishOrderDTO.getRideCardId()) || Objects.nonNull(finishOrderDTO.getVipId()) || Objects.nonNull(finishOrderDTO.getCouponUserId())) {
            String s = consumptionCardCoupon(finishOrderDTO.getUserId(), finishOrderDTO.getAccountId(), finishOrderDTO.getRideCardId(), finishOrderDTO.getVipId(), finishOrderDTO.getCouponUserId());
            finishOrderDTO.setRemark(finishOrderDTO.getRemark() + s);
        }

        String url = OperaPswConstant.ACCOUNT_OPERA_CLIENT_URL + ServiceUrlConstant.CLIENT_FINISH_ORDER;
        String success = "1";
        Map<String, Object> map = initRequestMap(login, finishOrderDTO);
        String body = null;
        try {
            body = HttpRequest.get(url).form(map).execute().body();
            log.info("调用客户端接口结束订单,请求参数={},客户端返回{}", JSON.toJSONString(map), body);
            JSONObject jsonObject = JSON.parseObject(body);
            if (!success.equals(jsonObject.get("ret").toString())) {
                throw new BaseException(jsonObject.get("msg").toString());
            }
            // 插入车辆操作日志
            machineOpLogService.batchAddRecord("关锁还车", "人工还车", true, "骑行单号：" + rideLog.getOrderNO(), Collections.singletonList(machineDao.getByMachineId(rideLog.getMachineId())));
        } catch (Exception e) {
            log.error("调用客户端接口结束订单失败,请求参数={},客户端返回{},exception={}", JSON.toJSONString(map), body, e.getMessage());
            return Result.error("调用客户端接口结束订单失败");
        }
        return Result.success();
    }

    @Override
    public Result editOrder(LoginInfo login, EditOrderDTO editOrderDTO) {
        if (editOrderDTO.getRideMoney() > 10000000 || editOrderDTO.getFineMoney() > 10000000) {
            return Result.error("改动金额过大，请确认后操作");
        }
        //修改订单前判断是否包含特殊字符
        if (Utf8Mb4Checker.containsUtf8mb4Chars(editOrderDTO.getRemark())) {
            throw new BaseException("备注含非法字符，请调整后再次发起");
        }
        String orderId = editOrderDTO.getOrderId();
        if (redisService.get(RedisConstant.LOCK_ORDER_MONEY + orderId) != null) {
            return Result.error("用户支付中,请稍后再试");
        }
        RideLog rideLog = rideLogDao.getByOrderNO(orderId);
        //校验订单相应信息
        String checkMsg = checkOrder(editOrderDTO, rideLog);
        if (checkMsg != null) {
            return Result.error(checkMsg);
        }
        User user = userDao.getByUserId(rideLog.getUserId());
        if (user == null) {
            return Result.error("用户不存在");
        }
        //用户余额
        Long money = user.getMoney() == null ? 0L : user.getMoney().longValue();
        //原始订单金额
        Long oldRideLogMoney = rideLog.getMoney() == null ? 0L : rideLog.getMoney().longValue();
        //修改的骑行费用
        Long rideMoney = editOrderDTO.getRideMoney();
        //修改的罚款费用
        Long fineMoney = editOrderDTO.getFineMoney();
        //修改后的总订单金额
        Long newRideLogMoney = rideMoney + fineMoney;
        //人工修改订单抵扣费用
        Long peopleModiftDeductionMoney = oldRideLogMoney - newRideLogMoney;
        String rideLogRemarkFormat = String.format("人工修改订单金额,修改前订单金额%s元,修改后骑行费用%s元,罚款费用%s元", toYuan(oldRideLogMoney), toYuan(rideMoney), toYuan(fineMoney));
        String rideLogRemark = rideLog.getRemark() + "," + rideLogRemarkFormat;
        //修改次数过多备注过长会导致插入失败，直接截断
        rideLogRemark = rideLogRemark.length() > 250 ? rideLogRemark.substring(0, 250) : rideLogRemark;
        boolean isPay = false;
        //用户余额
        Long newUserMoney;
        //用户欠款金额
        Long newUserRideMoney;
        Date now = new Date();
        Account account = accountDao.getByAccountId(rideLog.getAccountId());
        if (newRideLogMoney == 0L || money >= newRideLogMoney) {
            //余额够支付
            isPay = true;
            newUserMoney = money - newRideLogMoney;
            newUserRideMoney = 0L;
            //插入新表和旧表的流水记录
            userAccountLogDao.insert(new UserAccountLog(user.getUserId(), rideLog.getAccountId(), 0, 0, newRideLogMoney.intValue(), now, String.format("余额扣费%s元", toYuan(newRideLogMoney))));
            userDelayPayDao.insert(new UserDelayPay(user.getUserId(), rideLog.getAccountId(), newRideLogMoney.intValue(), new Date(), rideLog.getOrderNO()));
            //扣除用户的区域余额
            deductionUserAccountMoney(user, newRideLogMoney, rideLog.getAccountId(), now, orderId, login, editOrderDTO.getRemark(), oldRideLogMoney);
            rideUseLogDao.insert(new RideUseLog(null, rideLog.getOrderNO(), rideLog.getAccountId(), user.getAccountId(), newRideLogMoney.intValue(), newRideLogMoney.intValue(), 0, 0, 0, null, now, null));
        } else {
            //余额不够支付，用户余额不变，欠款金额变为新的订单金额
            newUserMoney = money;
            newUserRideMoney = newRideLogMoney;
        }
        if (!isPay) {
            //未支付状态，更新欠款
            userAccountMoneyDao.update(new UserAccountMoney(user.getUserId(), user.getPhone(), rideLog.getAccountId(), user.getAccountId(), newRideLogMoney, now.getTime(), 4, "人工修改订单欠款"), new UpdateWrapper<UserAccountMoney>().eq("userId", user.getUserId()).eq("accountId", rideLog.getAccountId()).eq("moneyType", 4));
        } else {
            //已支付状态，更新欠款为0
            userAccountMoneyDao.update(new UserAccountMoney(user.getUserId(), user.getPhone(), rideLog.getAccountId(), user.getAccountId(), 0L, now.getTime(), 4, "人工修改订单欠款补交"), new UpdateWrapper<UserAccountMoney>().eq("userId", user.getUserId()).eq("accountId", rideLog.getAccountId()).eq("moneyType", 4));
        }
        boolean repaymentArrears = (oldRideLogMoney - newRideLogMoney) > 0;
        arrearsLogDao.insert(new ArrearsLog(null, user.getUserId(), user.getPhone(), user.getName(), rideLog.getAccountId(), user.getAccountId(), account == null ? "" : account.getName(), orderId, repaymentArrears ? 2 : 1, repaymentArrears ? 5 : 2,
                Math.abs(peopleModiftDeductionMoney.intValue()), isPay ? peopleModiftDeductionMoney.intValue() : oldRideLogMoney.intValue(), isPay ? 0 : newUserRideMoney.intValue(), now.getTime(), null, login.getAccountUserId(), login.getName(), null, editOrderDTO.getRemark()));
        userDao.updateUserMoney(user.getUserId(), newUserMoney, newUserRideMoney, now);
        //修改两个骑行表的信息
        rideLogDao.updateRideLogByOrderNO(orderId, newRideLogMoney, rideLogRemark, isPay, now);
        rideLogDetailsDao.updateByOrderId(new RideLogDetailsUpdate(orderId, now, peopleModiftDeductionMoney, now, login.getAccountUserId(), login.getPhone(), login.getName(), rideLogRemarkFormat, isPay, rideMoney, fineMoney, newRideLogMoney));
        //订单金额修改日志
        rideOrderMoneyOpeLogDao.insert(new RideOrderMoneyOpeLog(rideLog.getAccountId(), rideLog.getOrderNO(), oldRideLogMoney.intValue(), newRideLogMoney.intValue(), now, user.getUserId(), user.getPhone(), login.getAccountUserId(), login.getPhone(), editOrderDTO.getRemark(), null, null, null, null, null));
        // 插入用户操作日志
        userOpLogService.insertSingle(UserOpLog.builder()
                .accountId(user.getAccountId())
                .opUser(login.getName())
                .opUserPhone(login.getPhone())
                .targetUser(user.getName())
                .targetUserPhone(user.getPhone())
                .operation("用户余额变动")
                .opType("清空欠款")
                .opPlatform(login.getLoginUserPlatform())
                .opTime(LocalDateTime.now())
                .detail("变动金额: " + peopleModiftDeductionMoney / 100d + "元")
                .result(true)
                .build());
        return Result.success();
    }

    @Override
    public RideLogDetailsVO queryByOrderId(String orderId) {
        RideLogDetailsVO rideLogDetailsVO = rideLogDetailsDao.queryByOrderId(orderId);
        if (rideLogDetailsVO != null) {
            List<RefundLog> refundLogs = refundLogDao.selectList(new QueryWrapper<RefundLog>().eq("orderId", orderId));
            //骑行费用退款
            long rideRefundMoney = 0L;
            //用户罚款退款
            long fineRefundMoney = 0L;
            //礼品卡退款
            long giftRefundMoney = 0L;
            for (RefundLog refundLog : refundLogs) {
                switch (refundLog.getChangeType()) {
                    case RefundLogConstant.REFUND_OF_CYCLING_EXPENSES:
                        //骑行费用退款
                        rideRefundMoney += refundLog.getChangeMoney();
                        break;
                    case RefundLogConstant.USER_PENALTY_REFUND:
                        //用户罚款退款
                        fineRefundMoney += refundLog.getChangeMoney();
                        break;
                    default:
                        break;
                }
            }

            /*查询礼品卡扣费*/
            List<UserAccountLog> userAccountLogs = userAccountLogDao.getGiftByRemark(rideLogDetailsVO.getUserId(), orderId);
            for (UserAccountLog userAccountLog : userAccountLogs) {
                giftRefundMoney += userAccountLog.getMoney();
            }

            List<RefundOrder> refundOrders = refundOrderService.getByBusinessNo(orderId);
            // 过滤出退款中状态
            Integer refundStatus = refundOrders.stream()
                    .filter(order -> order.getRefundStatus() == 0)
                    .findFirst()
                    .map(order -> 0)
                    .orElse(null);
            rideLogDetailsVO.setRefundStatus(refundStatus);
            rideLogDetailsVO.setRefundRideMoney((double) rideRefundMoney / 100);
            rideLogDetailsVO.setRefundFineMoney((double) fineRefundMoney / 100);
            rideLogDetailsVO.setIsRefundMoney((double) (rideRefundMoney + fineRefundMoney) / 100);
            rideLogDetailsVO.setRefundGiftCardMoney((double) giftRefundMoney / 100);
            rideLogDetailsVO.computeReturnable();
            if (CollectionUtils.isNotEmpty(refundLogs)) {
                RefundLog refundLog = refundLogs.get(refundLogs.size() - 1);
                rideLogDetailsVO.setRefundRemark(StringUtils.defaultString(refundLog.getRefundRemark(), " "));
            }

            /*新增订单费用申诉数据*/
            FeeAppeal feeAppeal = orderFeeAppealDao.getOrderNo(orderId);
            if (Objects.nonNull(feeAppeal)){
                rideLogDetailsVO.setUrl(feeAppeal.getUrl());
                rideLogDetailsVO.setType(feeAppeal.getType());
                rideLogDetailsVO.setRemark(feeAppeal.getRemark());
            }
            /**订单费用申诉统计相关信息*/
            FeeAppealInfoVO appealStats = orderFeeAppealService.getAppealStats(rideLogDetailsVO.getUserId());
            if (Objects.nonNull(appealStats)){
                rideLogDetailsVO.setFeeAppealInfoVO(appealStats);
            }
            //订单用户的其它数据统计(订单数、退款数、费用申述数)
            this.getUserStat(rideLogDetailsVO);
        }


        return rideLogDetailsVO;
    }

    @Override
    public Result<String> clearArrears(LoginInfo login, Integer userId) {
        User user = userDao.getByUserId(userId);
        if (user == null) {
            return Result.error("用户不存在");
        }
        int rideMoney = user.getRideMoney();
        if (rideMoney == 0) {
            return Result.error("用户无欠款");
        }
        if (rideMoney < 0) {
            return Result.error("用户欠款数据有误，请联系管理员处理");
        }
        Date now = new Date();
        RideLogDetails rideLogDetails = rideLogDetailsDao.queryLatestCyclingOrder(userId);
        if (rideLogDetails == null) {
            //旧欠款金额
            int oldRideLogMoney = user.getRideMoney();
            //新欠款金额
            int newRideLogMoney = 0;
            //无欠款订单，清除欠款
            boolean repaymentArrears = (oldRideLogMoney - newRideLogMoney) > 0;
            //清除区域欠款
            userAccountMoneyDao.update(new UserAccountMoney(user.getUserId(), user.getPhone(), -1, user.getAccountId(), 0L, now.getTime(), 4, "人工清除欠款"), new UpdateWrapper<UserAccountMoney>().eq("userId", user.getUserId()).eq("accountId", -1).eq("moneyType", 4));
            //插入欠款补交记录
            arrearsLogDao.insert(new ArrearsLog(null, user.getUserId(), user.getPhone(), user.getName(), -1, user.getAccountId(), "未知区域", "", repaymentArrears ? 2 : 1, repaymentArrears ? 5 : 2,
                    Math.abs(oldRideLogMoney), oldRideLogMoney, newRideLogMoney, now.getTime(), null, login.getAccountUserId(), login.getName(), null, "人工清除欠款"));
            userDao.updateUserMoney(user.getUserId(), Long.valueOf(user.getMoney()), 0L, now);

            // 插入用户操作日志
            userOpLogService.insertSingle(UserOpLog.builder()
                    .accountId(user.getAccountId())
                    .opUser(login.getName())
                    .opUserPhone(login.getPhone())
                    .targetUser(user.getName())
                    .targetUserPhone(user.getPhone())
                    .operation("用户余额变动")
                    .opType("清空欠款")
                    .opPlatform(login.getLoginUserPlatform())
                    .opTime(LocalDateTime.now())
                    .detail("变动金额: " + oldRideLogMoney / 100d + "元")
                    .result(true)
                    .build());
        } else {
            if (rideLogDetails.getPayTime() != null) {
                return Result.error("用户欠款订单数据有误，请联系管理员处理");
            }
            //有欠款订单，走修改订单逻辑，修改为0
            editOrder(login, new EditOrderDTO(rideLogDetails.getOrderId(), 0L, 0L, null, "人工清除欠款"));
        }
        return Result.success();
    }


    /**
     * 获取用户有效卡券
     *
     * @param userId    用户id
     * @param accountId 区域id
     * @return Result<EfficientCardVO>
     */
    @Override
    public Result<EfficientCardVO> getEfficientCard(Integer userId, Integer accountId) {
        /*查询当前正在骑行的订单*/
        MachineBorrow machineBorrow = machineBorrowDao.getByUserId(userId);
        if (Objects.isNull(machineBorrow)) {
            return Result.error("不存在骑行中的订单");
        }
        RideLog rideLog = rideLogDao.getByOrderNO(machineBorrow.getOrderNO());
        if (Objects.isNull(rideLog)) {
            log.info("{}-订单不存在", userId);
            return Result.error("订单不存在，请联系客服");
        }
        if (Objects.isNull(rideLog.getStartTime())){
            return Result.error("订单异常，请联系客服处理");
        }

        EfficientCardVO efficientCardVO = new EfficientCardVO(rideCardDao.getEfficientRideCard(userId, accountId),
                vipCardDao.getEfficientVipCard(userId, accountId),
                couponDao.getEfficientCoupon(userId, accountId));
        //校验骑行卡是否在当前使用范围内
        if (Objects.nonNull(efficientCardVO.getRideCard())) {
            RideCardOfUserByOrderVO rideCard = efficientCardVO.getRideCard();
            if (Objects.nonNull(rideCard.getValidWeek())) {
                List<String> list = Arrays.asList(rideCard.getValidWeek().split(","));
                List<Integer> validWeekIntList = list.stream()
                        .map(Integer::parseInt)
                        .collect(Collectors.toList());
                if (validWeekIntList.size() < 7 && !validWeekIntList.contains(DateUtil.dayOfWeek(rideLog.getStartTime()))) {
                    efficientCardVO.setRideCard(null);
                    return Result.success(efficientCardVO);
                }
            }
            //校验骑行卡是否在当前使用范围内
            if (Objects.nonNull(rideCard.getVaildTime()) && !isTimeInRanges(rideLog.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalTime(), rideCard.getVaildTime().split(";"))) {
                efficientCardVO.setRideCard(null);
                return Result.success(efficientCardVO);
            }
        }
        return Result.success(efficientCardVO);
    }

    @Override
    public String downloadRideLogDetail(RideLogDetailsDownloadDTO downloadDTO) {

        File tempFile = csvExportUtils.getTempFile();
        try {
            File file = downloadRideLogDetail(downloadDTO, tempFile);
            String uploadFileName = UUID.randomUUID().toString().replaceAll("-", "");
            return csvExportUtils.uploadZip(tempFile, uploadFileName, file);
        } finally {
            FileUtil.del(tempFile);
        }
    }

    public File downloadRideLogDetail(RideLogDetailsDownloadDTO downloadDTO, File tempFile) {

        IntSupplier countFn = () -> rideLogDetailsDao.selectDataCount(BeanUtils.copyProperties(downloadDTO, RideLogDetailsQueryDTO.class));
        ExportHandler<RideLogDetails> exportHandler = baseEntity -> {
            RideLogDetailsQueryDTO dto = BeanUtils.copyProperties(downloadDTO, RideLogDetailsQueryDTO.class);
            dto.setStartTime(baseEntity.getStartTime());
            dto.setEndTime(baseEntity.getEndTime());
            //第一次数据库查询的阈值
//            dto.setSize(config.getBigDataQueryCount());
            return rideLogDetailsDao.selectList(dto);
        };
        Date startTime = downloadDTO.getStartTime();
        Date endTime = downloadDTO.getEndTime();
        return csvExportUtils.exportBigCsvUseDynamicDate(exportHandler, RideLogDetails.class, countFn,
                startTime, endTime, tempFile, downloadDTO.getFileName());
    }

    private String toYuan(Long money) {
        return String.format("%.2f", ((double) money / 100D));
    }

    /**
     * 扣除用户区域余额
     *
     * @param user            用户
     * @param money           需要扣除的金额 (单位:分)
     * @param accountId       需要优先扣除金额的区域
     * @param time            扣款时间
     * @param orderId         订单编号
     * @param login           操作人信息
     * @param oldRideLogMoney 旧订单金额
     */
    private void deductionUserAccountMoney(User user, Long money, Integer accountId, Date time, String orderId, LoginInfo login, String remark, Long oldRideLogMoney) {
        if (money == 0L) {
            //0元结束不需要去扣区域余额
            return;
        }
        Integer originalAccountId = accountId;
        //获取用户初始区域id
        UserToAgent userToAgent = userToAgentDao.getByUserId(user.getUserId());
        if (userToAgent != null) {
            originalAccountId = userToAgent.getFirstId();
        }
        //已扣除金额
        Long isDeductionMoney = 0L;
        //品牌id
        Integer brandId = user.getAccountId();
        UserAccountMoney userAccountMoney = userAccountMoneyDao.selectOne(new QueryWrapper<UserAccountMoney>().eq("userId", user.getUserId()).eq("accountId", accountId).eq("moneyType", UserAccountMoneyConstant.BALANCE));

        isDeductionMoney = updateUserAccountMoney(user, money, accountId, time, orderId, login, remark, isDeductionMoney, brandId, userAccountMoney, originalAccountId, oldRideLogMoney);
        if (!isDeductionMoney.equals(money)) {
            //需要优先扣除的区域余额不够扣(少部分情况)，去扣除其他区域的余额
            List<UserAccountMoney> userAccountMoneyList = userAccountMoneyDao.selectList(new QueryWrapper<UserAccountMoney>().eq("userId", user.getUserId()).eq("moneyType", UserAccountMoneyConstant.BALANCE).orderByDesc("money").ne("accountId", accountId));
            for (UserAccountMoney userAccount : userAccountMoneyList) {
                isDeductionMoney = updateUserAccountMoney(user, (money - isDeductionMoney), userAccount.getAccountId(), time, orderId, login, remark, isDeductionMoney, brandId, userAccount, originalAccountId, oldRideLogMoney);
                if (isDeductionMoney.equals(money)) {
                    break;
                }
            }
        }

    }

    private Long updateUserAccountMoney(User user, Long money, Integer accountId, Date time, String orderId, LoginInfo login, String remark, Long isDeductionMoney, Integer brandId, UserAccountMoney userAccountMoney, Integer originalAccountId, Long oldRideLogMoney) {
        if (userAccountMoney == null) {
            return isDeductionMoney;
        }
        Long accountMoney = userAccountMoney.getMoney() == null ? 0L : userAccountMoney.getMoney();
        if (accountMoney == 0) {
            return isDeductionMoney;
        }
        //抵扣前金额
        Long isDeductionMoneyTemp = isDeductionMoney;
        //当前实际扣除的金额
        Long actualMoney;
        if (accountMoney >= money) {
            //区域余额够扣
            isDeductionMoney += money;
            actualMoney = money;
        } else {
            actualMoney = accountMoney;
            isDeductionMoney += actualMoney;
        }
        userCapitalChangeDao.insert(new UserCapitalChange(null, user.getUserId(), user.getPhone(), user.getName(), accountId, user.getAccountId(), userAccountMoney.getMoney(), actualMoney, userAccountMoney.getMoney() - actualMoney, 2, 1, 5, orderId, login.getAccountUserId(), login.getPhone(), login.getName(), orderId, orderId, time.getTime(), remark, originalAccountId));
        userAccountMoney.setUpdateTime(time.getTime()).setMoney(accountMoney - actualMoney).setRemark(orderId);
        Account account = accountDao.getByAccountId(userAccountMoney.getAccountId());
        arrearsLogDao.insert(new ArrearsLog(null, user.getUserId(), user.getPhone(), user.getName(), userAccountMoney.getAccountId(), user.getAccountId(), account == null ? "未知区域" : account.getName(), orderId, 2, 6, actualMoney.intValue(), oldRideLogMoney.intValue() - isDeductionMoneyTemp.intValue(), oldRideLogMoney.intValue() - isDeductionMoneyTemp.intValue() - isDeductionMoney.intValue(), time.getTime(), null, login.getAccountUserId(), login.getName(), "人工修改订单扣除区域余额", remark));
        userAccountMoneyDao.update(userAccountMoney, new UpdateWrapper<UserAccountMoney>().eq("userId", user.getUserId()).eq("accountId", accountId).eq("moneyType", UserAccountMoneyConstant.BALANCE));
        balanceChangeDao.insert(new BalanceChange(null, brandId, user.getPhone(), user.getName(), accountId, user.getAccountId(), accountMoney, actualMoney, accountMoney - actualMoney, 2, 1, 5, time.getTime(), "人工修改订单扣除区域余额", orderId));
        moneyOpeLogDao.insert(new MoneyOpeLog(brandId, 1, 0, actualMoney.intValue(), time, user.getUserId(), user.getPhone(), login.getAccountUserId(), login.getPhone(), remark));
        return isDeductionMoney;
    }

    /**
     * 校验订单相应信息
     *
     * @param editOrderDTO
     * @return
     */
    private String checkOrder(EditOrderDTO editOrderDTO, RideLog rideLog) {
        if (rideLog == null) {
            return "订单不存在";
        }
        if (rideLog.getEndTime() == null) {
            return "订单未结束，请先结束订单";
        }
        if (rideLog.getPayTime() != null) {
            return "已支付订单不允许修改";
        }
        if (editOrderDTO.getFineMoney() < 0) {
            return "罚款费用有误";
        }
        if (editOrderDTO.getRideMoney() < 0) {
            return "骑行费用有误";
        }
        return null;
    }

    /**
     * 初始化请求map
     *
     * @param login
     * @param finishOrderDTO
     * @return
     */
    private Map<String, Object> initRequestMap(LoginInfo login, FinishOrderDTO finishOrderDTO) {
        long date = System.currentTimeMillis();
        String param = "youqu";
        Map<String, Object> map = new HashMap<>();
        map.put("userId", finishOrderDTO.getUserId());
        map.put("money", finishOrderDTO.getMoney());
        map.put("remark", finishOrderDTO.getRemark());
        //类型 为2则是人工结束订单，3是超时结束订单
        map.put("type", 2);
        map.put("sign", MD5PasswordUtil.MD5("TBIT" + date + param));
        map.put("date", date);
        map.put("param", param);
        map.put("operatorId", login.getAccountUserId());
        map.put("operatorName", login.getName());
        map.put("operatorPhone", login.getPhone());
        return map;
    }


    /**
     * 判断当前时间是否在指定时间段内
     *
     * @param now        当前时间
     * @param timeRanges 时间段
     * @return boolean
     */
    public static boolean isTimeInRanges(LocalTime now, String[] timeRanges) {
        for (String range : timeRanges) {
            String[] times = range.split("-");
            LocalTime start = LocalTime.parse(times[0]);
            LocalTime end = LocalTime.parse(times[1]);

            if (!now.isBefore(start) && !now.isAfter(end)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 消耗卡券
     *
     * @param userId     用户id
     * @param accountId  区域id
     * @param rideCardId 骑行卡id
     * @param couponUserId 优惠券id
     */
    @Override
    public String consumptionCardCoupon(Integer userId, Integer accountId, Integer rideCardId, Integer vipId, Integer couponUserId) {

        StringBuilder cardName = new StringBuilder();
        cardName.append("，人工使用卡券：");

        /*骑行卡*/
        RideCardOfUserByOrderVO rideCard = rideCardDao.getEfficientRideCard(userId, accountId);
        if (Objects.nonNull(rideCard)) {
            //记录骑行卡名称
            cardName.append(rideCard.getName());
            //消耗骑行卡次数
            UserMember userMember = rideCardDao.selectByUserIdAndAccountId(userId, accountId);
            HashMap<String, Object> map = new HashMap<>();
            map.put("userId", userId);
            map.put("rideCardId", rideCardId);
            map.put("type", 1);
            map.put("orderNO", userMember.getOrderNO());
            rideCardDao.updateuserRideCard(map);
            if (Objects.equals(rideCard.getUserType(), 1)){
                if ( NumberUtil.sub(rideCard.getMaxRide() - userMember.getCurrentNum()).intValue() <= 0) {
                    throw new BaseException("可使用次数超过每日上限,不可添加使用次数");
                }
                rideCardDao.updateCurrentNumByOrderNo(userMember.getOrderNO(), userMember.getCurrentNum() + 1);
            }
            cardName.append("骑行卡:(").append(rideCard.getName()).append("),");
        }

        /*会员卡*/
        if (Objects.nonNull(vipId)){
            VipCard vipCard = vipCardDao.selectByVipId(vipId);
            if (Objects.nonNull(vipCard)){
                cardName.append("会员卡:(").append(vipCard.getName()).append("),");
            }
        }

        /*优惠券*/
        if (Objects.nonNull(couponUserId)){
            Coupon coupon = couponDao.getByCouponUserId(couponUserId);
            if (Objects.nonNull(coupon)){
                cardName.append("优惠券:(").append(coupon.getName()).append(")");
            }
            couponDao.userCouponEdit(couponUserId, userId);
        }

        return cardName.toString();
    }


    /**
     * @description: 订单用户的其它数据统计(订单数、退款数、费用申述数)
     * @author: mo.shanyong
     * @date: 2025/5/19 10:41
     * @param: rideLogDetailsVO
     */
    private void getUserStat(RideLogDetailsVO rideLogDetailsVO){
        User user = userDao.getByUserId(rideLogDetailsVO.getUserId());
        //上一步，在订单费用申述接口已经有做统计了，如果没有再去查询
        rideLogDetailsVO.setUserOrderNum(Objects.nonNull(rideLogDetailsVO.getFeeAppealInfoVO()) ? rideLogDetailsVO.getFeeAppealInfoVO().getRideLogCount() :
                rideLogDetailsDao.countByUserId(rideLogDetailsVO.getUserId()));
        rideLogDetailsVO.setReturnMoneyNum(userCapitalChangeDao.selectUserFundsFlowCount(
                null,null,null,null,user.getPhone(),null,null,8
        ));
        rideLogDetailsVO.setUserFeeAppealNum(Objects.nonNull(rideLogDetailsVO.getFeeAppealInfoVO()) ? rideLogDetailsVO.getFeeAppealInfoVO().getAppealCount() :
                orderFeeAppealDao.countByUserId(rideLogDetailsVO.getUserId()));
    }

}
